//
//  JGMusicAnimationViewWaveform.m
//  JGMusicAnimation
//
//  Created by Mei Jigao on 16/3/11.
//  Copyright © 2016年 MEETStudio. All rights reserved.
//

#import "JGMusicAnimationViewWaveform.h"
#import "JGMusicAnimationConfig.h"
#import <Accelerate/Accelerate.h>
#import <AVFoundation/AVFoundation.h>

@interface JGMusicAnimationViewWaveform () {
    
    // buffers
    float *dataBuffer;
    size_t bufferCapacity;
    size_t bufferIndex;
    
    // draw
    float *heightsByX;
}

@property (nonatomic, strong) CADisplayLink *displaylink;

@end

@implementation JGMusicAnimationViewWaveform

+ (Class)layerClass {
    
    return [CAGradientLayer class];
}

#pragma mark - init & dealloc
- (instancetype)init {
    
    self = [super init];
    if (self) {
        
        [self setup];
    }
    
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    
    self = [super initWithCoder:aDecoder];
    if (self) {
        
        [self setup];
    }
    
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    if (self) {
        
        [self setup];
    }
    
    return self;
}

- (void)setup {
    
    [self setBackgroundColor:[UIColor clearColor]];
    
    // buffer
    dataBuffer = (float *)malloc(JGMAViewDrawFrames * sizeof(float));
    bufferCapacity = JGMAViewDrawFrames;
    bufferIndex = 0;
    
    // create buffers
    heightsByX = (float *)calloc(sizeof(float), bufferCapacity);
    
    // draw timer
    _displaylink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateHeights)];
    [_displaylink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    [_displaylink setPaused:YES];
    
    [self setDefaultProperties];
}

- (void)removeDisplayTimer {
    
    [_displaylink invalidate], _displaylink = nil;
}

- (void)dealloc {
    
    JGMALog(@"");
    [_displaylink invalidate], _displaylink = nil;
    
    [self freeBuffersIfNeeded];
}

#pragma mark - Properties
- (void)setDefaultProperties {
    
    [self setDrawInterval:JGMAViewDrawIntervalDefault];
    [self setGain:JGMAViewGainDefault];
    [self setLineWidth:1.f];
    
    NSArray *colors = [NSArray arrayWithObjects:
                       [UIColor colorWithRed:255 / 255.0 green:86 / 255.0 blue:20 / 255.0 alpha:1],
                       [UIColor colorWithRed:255 / 255.0 green:250 / 255.0 blue:71 / 255.0 alpha:1],
                       [UIColor colorWithRed:249 / 255.0 green:59 / 255.0 blue:4 / 255.0 alpha:1],
                       [UIColor colorWithRed:249 / 255.0 green:59 / 255.0 blue:4 / 255.0 alpha:1],
                       [UIColor colorWithRed:255 / 255.0 green:250 / 255.0 blue:71 / 255.0 alpha:1],
                       [UIColor colorWithRed:249 / 255.0 green:59 / 255.0 blue:4 / 255.0 alpha:1],
                       [UIColor colorWithRed:240 / 255.0 green:24 / 255.0 blue:0 / 255.0 alpha:1],
                       [UIColor colorWithRed:251 / 255.0 green:114 / 255.0 blue:32 / 255.0 alpha:1],
                       [UIColor colorWithRed:249 / 255.0 green:59 / 255.0 blue:4 / 255.0 alpha:1],
                       [UIColor colorWithRed:240 / 255.0 green:24 / 255.0 blue:0 / 255.0 alpha:1],
                       nil];
    [self setColors:colors];
}

- (void)setDrawInterval:(CGFloat)drawInterval {
    
    CGFloat interval = MAX(MIN(JGMAViewDrawIntervalMax, drawInterval), JGMAViewDrawIntervalMin);
    
    [_displaylink setFrameInterval:interval];
}

- (void)setGain:(CGFloat)gain {
    
    _gain = gain;
}

- (void)setLineWidth:(CGFloat)lineWidth {
    
    _lineWidth = lineWidth;
}

- (void)setColors:(NSArray *)colors {
    
    _colors = colors;
    
    CAGradientLayer *gradientLayer = (id)[self layer];
    
    // 设置颜色线条分布的方向
    gradientLayer.startPoint = CGPointMake(0.0, 0.0);
    gradientLayer.endPoint = CGPointMake(0.0, 1.0);
    
    // 创建颜色数组
    NSMutableArray *tmp = [NSMutableArray array];
    for (NSInteger i = 0; i < [_colors count]; i++) {
        
        UIColor *color = [_colors objectAtIndex:i];
        [tmp addObject:(id)color.CGColor];
    }
    
    [gradientLayer setColors:[NSArray arrayWithArray:tmp]];
}

#pragma mark - Timer Callback
- (void)updateHeights {
    
    [self _refreshDisplay];
}

#pragma mark - Update Buffers
- (void)setSampleData:(float *)data length:(int)length {
    
    // fill the buffer.
    int read = (int)(bufferCapacity - bufferIndex);
    if (read > length) {
        
        memcpy((float *)dataBuffer + bufferIndex, data, length * sizeof(float));
        bufferIndex += length;
    }
    else {
        
        memcpy((float *)dataBuffer + bufferIndex, data, read * sizeof(float));
        
        // reset index.
        bufferIndex = 0;
        
        for (NSUInteger i = 0; i < bufferCapacity; i++) {
            
            // height
            float avg = dataBuffer[i] * _gain;
            
            heightsByX[i] = avg * CGRectGetHeight(self.frame) * 0.5;
        }
    }
}

- (void)shouldDrawing:(BOOL)draw {
    
    [_displaylink setPaused:!draw];
}

- (void)updateWithBuffer:(float *)buffer bufferSize:(UInt32)bufferSize {
    
    [self setSampleData:buffer length:bufferSize];
    if ([_displaylink isPaused]) {
        
        [_displaylink setPaused:NO];
    }
}

#pragma mark - Drawing
- (void)drawRect:(CGRect)rect {
    
    if ([self isHidden]) {
        return;
    }
    
    // 折线参数
    CGFloat halfH = CGRectGetHeight(rect) * 0.5;
    CGFloat gap = ceil(CGRectGetWidth(rect) / (bufferCapacity - 1));
    
    // 折线路径
    CGFloat x = 0;
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, x, halfH);
    
    for (NSUInteger i = 1; i < bufferCapacity; i++) {
        
        x += gap;
        CGPathAddLineToPoint(path, nil, x, halfH - heightsByX[i]);
    }
    
    // 折线的Layer
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    [shapeLayer setPath:path];
    [shapeLayer setStrokeColor:[[UIColor whiteColor] CGColor]];
    [shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
    [shapeLayer setLineWidth:_lineWidth];
    
    // 绘制完整折线
    shapeLayer.strokeStart = 0;
    shapeLayer.strokeEnd = 1.0;
    self.layer.mask = shapeLayer;
    
    //path释放
    CGPathRelease(path);
}

- (void)_refreshDisplay {
    
    [self setNeedsDisplay];
}

- (void)clear {
    
    [self shouldDrawing:NO];
    
    for (NSUInteger i = 1; i < bufferCapacity; i++) {
        
        dataBuffer[i] = 0;
        heightsByX[i] = 0;
    }
    
    [self setNeedsDisplay];
}

#pragma mark - Free
- (void)freeBuffersIfNeeded {
    
    if (heightsByX) {
        free(heightsByX);
    }
}

#pragma mark - end

@end
